home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
FishMarket 1.0
/
FishMarket v1.0.iso
/
fishies
/
426-450
/
disk_429
/
tripppin
/
source
/
trip.c
< prev
next >
Wrap
C/C++ Source or Header
|
1992-05-06
|
12KB
|
509 lines
/* TO ADD:
choice of aggressive or defensive computer play (aggressive = assume you
will fuck up if given the chance) ?
requester to tell you the rules ?
*/
/*
This is sort of the game of TRIPPPLES ® that was marketed by some game
company I forget which, and I can't find it in the stores so this
implementation is based purely on my memory of having played the game years
and years ago ... the computer opponent uses a straightforward lookahead
strategy of variable depth. I wrote this mainly for practice with the
graphics.library which I hadn't hardly used much yet, and just to prove I
COULD write an honest-to-god event loop ... most everything else I've been
writing has been CLI utilities ... It was easier than I expected; day 1 got
the board displayed pretty well (the tricky part was keeping everything
proportioned right on both interlaced and non-laced displays), day 2 I made
player tokens that could be moved around with the mouse (took some trial and
error to get the hang of using Bobs), the next evening got it to know all the
rules of the game, and display the game status in the right margin, another
evening got the computer opponent working, including the task synchronization
... after that it was just a matter of adding bells and whistles, like
putting arrowheads on the arrows, adding a menu, flashing X's to show where
the pieces are trying to go, the suggest-a-move and take-back-a-move
features, stuff to make it adapt gracefully to different sized fonts ...
After the first release users gave me some suggestions which prompted me to
add an option to turn off the prevention of loops, and stifle the sprites
when the window is inactive or the menus are up. I also found two bugs.
This program is written by Paul Kienitz and is placed in the public domain.
*/
#include <hardware/intbits.h>
#include <exec/interrupts.h>
#include <libraries/dosextens.h>
#include "trip.h"
import void ShowBoard(), TellTurn(), LiftBob(), DropBob(), DrawSquare(),
DragBob(), Play(), MakePrettyPictures(),
DropPrettyPictures(), KillAnts(), Ding();
import adr GfxBase, IntuitionBase;
import long startedthinking, Now();
piece oo, bb, *turn;
history ohist, bhist;
ubyte board[8][8]; /* grid of 8 bit masks */
bool won = false, bluefirst = false, looprevent = true;
bool thinking = false, abort_think = false, notadrill;
struct Message *wbs; /* Workbench startup message */
short difficulty = 3, depth, thinkx, thinky, suggx, suggy, oldpri;
short sigdone = -1, sigkillme = -1; /* child -> parent */
struct Task *child, *parent;
void VBsignal_func();
struct Interrupt VBsignal = {
{ null, null, NT_INTERRUPT, 6, "Tripppin signaler" }, /* node */
null, /* data - will be address of task to signal */
VBsignal_func /* code */
};
void Die(s) str s;
{
if (child) {
RemIntServer(INTB_VERTB, &VBsignal);
Signal(child, bit(sigdie));
Wait(bit(sigkillme));
DeleteTask(child);
}
if (~sigdone)
FreeSignal((long) sigdone);
if (~sigkillme)
FreeSignal((long) sigkillme);
KillAnts();
DumpPrettyPictures();
if (IntuitionBase)
CloseLibrary(IntuitionBase);
if (GfxBase)
CloseLibrary(GfxBase);
if (s && Output())
Write(Output(), s, (long) strlen(s));
if (wbs) {
Forbid();
ReplyMsg(wbs);
} else
SetTaskPri(parent, (long) oldpri);
Exit(s ? 20L : 0L);
}
#asm
public _VBsignal_func,_LVOSignal
_VBsignal_func: move.l #sigf_tof,d0
move.l 4,a6 ; is_Data (task addr) already in a1
jsr _LVOSignal(a6)
moveq #0,d0 ; set the Z bit
rts ; don't preserve a6 cause pri < 10
#endasm
/* This stupid interrupt server really serves no purpose any more, all it
does could be done as well by IntuiTicks. But it's here so I'm not going to
bother to take it out. */
void Init()
{
long d[3];
void ThinkerTask();
DateStamp(d);
srand((int) ((d[0] << 2) + (d[1] << 4) + d[2]));
if (!(GfxBase = (adr) OpenLibrary("graphics.library", 0L)))
Die("No graphics!!\n");
if (!(IntuitionBase = OpenLibrary("intuition.library", 0L)))
Die("No intuition!!\n");
MakePrettyPictures();
if (!~(sigdone = AllocSignal(-1L)) || !~(sigkillme = AllocSignal(-1L)))
Die("Couldn't allocate signals!\n");
if (!(child = CreateTask("Thinkin and Trippin", 0L, ThinkerTask, 8000L)))
Die("Couldn't create subtask!\n");
parent = FindTask(null);
VBsignal.is_Data = (adr) parent;
AddIntServer(INTB_VERTB, &VBsignal);
oo.hist = &ohist;
bb.hist = &bhist;
oo.other = &bb;
bb.other = &oo;
oo.machine = false;
bb.machine = true;
}
void StopThinking()
{
if (thinking) {
abort_think = true;
Wait(bit(sigdone)); /* thinking = false */
abort_think = false;
}
}
void StartThinking()
{
StopThinking();
suggx = suggy = -1;
abort_think = false;
notadrill = turn->machine;
depth = notadrill ? difficulty : 3;
/* just an initial value ^^^ goes up with time */
startedthinking = Now();
thinking = true;
Signal(child, bit(sigthink));
}
short xgoals[6] = { 4, 6, 6, 1, 1, 3 };
short ygoals[6] = { 7, 6, 1, 1, 6, 7 };
void SetGoal(p) piece *p;
{
short i;
i = (p == &oo ? p->reached + 1 : 4 - p->reached);
p->goalx = xgoals[i];
p->goaly = ygoals[i];
}
short xoffs[8] = { 0, 1, 1, 1, 0, -1, -1, -1 };
short yoffs[8] = { -1, -1, 0, 1, 1, 1, 0, -1 };
#define NX(x, i) ((x) + xoffs[i])
#define NY(y, i) ((y) + yoffs[i])
void Allow(from, tu) piece *from, *tu;
{
register short i, x , y;
ubyte r = 0;
for (i = 0; i < 8; i++) {
x = NX(tu->x, i);
y = NY(tu->y, i);
if (!(x & ~7) && !(y & ~7) && !(x == from->x && y == from->y))
r |= 1 << i;
}
tu->allowed = r & board[from->x][from->y];
}
void Restart()
{
Shuffle();
KillAnts();
oo.x = 4;
bb.x = 3;
oo.y = bb.y = 7;
Allow(&bb, &oo);
Allow(&oo, &bb);
oo.reached = bb.reached = 0;
SetGoal(&oo);
SetGoal(&bb);
ohist.top = bhist.top = HISTORY - 1;
ohist.count = bhist.count = 0;
turn = bluefirst ? &bb : &oo;
bluefirst = !bluefirst;
won = false;
StartThinking();
}
void Mooove(who, x, y) piece *who; short x, y;
{
register history *gh = who->hist;
if (gh->top < HISTORY - 1)
gh->top++;
else gh->top = 0;
if (gh->count < HISTORY)
gh->count++;
gh->hx[gh->top] = who->x;
gh->hy[gh->top] = who->y;
gh->madegoal[gh->top] = x == who->goalx && y == who->goaly;
who->x = x;
who->y = y;
Allow(who, who->other);
if (gh->madegoal[gh->top])
if (!(won = ++who->reached > 4))
SetGoal(who);
}
bool TryMove(who, x, y) piece *who; short x, y;
{
short i, xd, yd;
xd = x - who->x;
yd = y - who->y;
if (!xd && !yd)
return true; /* give player another try */
if (xd > 1 || xd < -1 || yd > 1 || yd < -1)
return false; /* moved too far */
if (!xd)
i = yd > 0 ? 4 : 0;
else if (xd > 0)
i = 2 + yd;
else
i = 6 - yd;
if (!(who->allowed & (1 << i)))
return false; /* forbidden move */
KillAnts();
StopThinking();
Mooove(who, x, y);
turn = turn->other;
if (!won)
StartThinking();
TellTurn();
return true;
}
#define max(a, b) ((a) > (b) ? (a) : (b))
#define abs(a) ((a) < 0 ? -(a) : (a))
short WayToGo(p) piece *p; /* Way To Go, Dexter Riley! */
{
register short d, dx, dy;
dx = abs(p->x - p->goalx);
dy = abs(p->y - p->goaly);
d = max(dx, dy);
if (p->reached < 4)
d += 5 * (4 - p->reached) - 2;
return d;
}
/* true if p's last move makes a forbidden loop - twice through is okay,
starting on a third time through is forbidden. Does not detect loops longer
than HISTORY / 2. */
bool Loopin(p) piece *p;
{
short len, pt, ot, pi, oi, n;
history *hp = p->hist, *ho = p->other->hist;
for (len = 2; len <= HISTORY >> 1; len++) {
if (hp->count < len << 1)
break;
pt = (hp->top + HISTORY + 1 - len) % HISTORY;
ot = (ho->top + HISTORY + 1 - len) % HISTORY;
if (p->other->x == ho->hx[ot] && p->other->y == ho->hy[ot]
&& p->x == hp->hx[pt] && p->y == hp->hy[pt]) {
/* possible loop -- confirm: */
pi = hp->top;
oi = ho->top;
for (n = 0; n < len; n++) {
pt = (pt + HISTORY - 1) % HISTORY;
ot = (ot + HISTORY - 1) % HISTORY;
if (hp->hx[pt] != hp->hx[pi] || hp->hy[pt] != hp->hy[pi] ||
ho->hx[ot] != ho->hx[oi] || ho->hy[ot] != ho->hy[oi])
break;
pi = (pi + HISTORY - 1) % HISTORY;
oi = (oi + HISTORY - 1) % HISTORY;
}
if (n >= len)
return true;
}
}
return false;
}
/* here we have the recursive lookahead strategy -- this function tries all
the moves available to player "me" against player "it" and sets rx and ry to
the best move available to "me", and returns a number that rates how well off
"me" ends up. It abandons computation and returns meaningless values if the
global variable abort_think ever becomes true. It does recursive lookahead
to depth "d". */
#define EXTREME 9999
short PickBestMove(d, me, it, rx, ry) short d; piece *me, *it; short *rx, *ry;
{
short i, besti, best = -EXTREME, t, go, bgo = 999;
short otop, ntop, oxx, oyy, oco;
piece p, *oooth;
history *h = me->hist;
if (!me->allowed)
return -EXTREME; /* I lose */
d--;
if (rx) {
t = 0;
for (i = 0; i <= 7; i++)
if (me->allowed & (1 << i)) {
t++;
*rx = NX(me->x, i);
*ry = NY(me->y, i);
}
if (t == 1) return 0; /* only legal move, skip other thinking */
}
otop = h->top;
oco = h->count;
if (h->count < HISTORY)
h->count++;
if (otop < HISTORY - 1)
ntop = otop + 1;
else ntop = 0;
h->top = ntop;
oxx = h->hx[ntop];
oyy = h->hy[ntop];
h->hx[ntop] = me->x;
h->hy[ntop] = me->y;
oooth = it->other;
it->other = &p;
for (i = 0; i <= 7; i++)
if (me->allowed & (1 << i)) {
if (abort_think)
return 0;
p = *me;
p.x = NX(p.x, i);
p.y = NY(p.y, i);
if (p.x == p.goalx && p.y == p.goaly)
if (++p.reached > 4) {
if (rx)
*rx = p.x, *ry = p.y;
best = EXTREME; /* I win */
break;
} else SetGoal(&p);
if (d <= 0)
t = WayToGo(it) - WayToGo(&p);
else {
ubyte a = it->allowed;
Allow(&p, it);
t = - PickBestMove(d, it, &p, null, null);
it->allowed = a;
} /* VVV oops, forgot that! */
if (rx && looprevent && notadrill && Loopin(&p))
t -= EXTREME << 1;
go = WayToGo(&p);
if (t > best || (t == best && go < bgo)) {
best = t;
besti = i;
bgo = go;
if (rx)
*rx = p.x, *ry = p.y;
}
}
it->other = oooth;
h->hx[ntop] = oxx;
h->hy[ntop] = oyy;
h->count = oco;
h->top = otop;
return best;
}
/* the following is started up as a separate task to calculate the best move.
When it gets the "sigthink" signal it looks in the global variables for what
to think about. It signals the parent when finished or aborted. When not
aborted it puts the move that the player whose turn it is should make in the
global vars thinkx and thinky. */
void ThinkerTask()
{
piece who, whom;
history wo, wm;
geta4();
for (;;) {
if (Wait(bit(sigthink) | bit(sigdie)) & bit(sigdie)) {
Signal(parent, bit(sigkillme));
Wait(0L);
}
thinking = true;
who = *turn;
whom = *who.other;
who.other = &whom;
whom.other = &who;
wo = *who.hist;
wm = *whom.hist;
who.hist = &wo;
whom.hist = &wm;
PickBestMove(depth, &who, &whom, &thinkx, &thinky);
Forbid();
thinking = false;
Signal(parent, bit(sigdone));
Permit();
}
}
void MachineMove()
{
piece *who;
short oldx, oldy;
oldx = turn->x;
oldy = turn->y;
Mooove(turn, thinkx, thinky);
LiftBob(turn);
DrawSquare(oldx, oldy); /* erase old image from rastport */
DropBob(turn); /* draw in new position */
turn = turn->other;
if (!won)
StartThinking();
TellTurn();
}
long _main()
{
struct Process *me = ThisProcess();
if (!me->pr_CLI) {
WaitPort(&me->pr_MsgPort);
wbs = GetMsg(&me->pr_MsgPort);
}
Init();
Restart();
ShowBoard();
if (me->pr_Task.tc_Node.ln_Pri <= 0)
oldpri = SetTaskPri(me, 1L);
Play();
Die(null);
}